<?php

function cobweb_getErrMsg($ierr)
{
	switch($ierr)
	{
	case  0:   return "Success";
	case -2:   return "Key is not exist";
	case -1:   return "System error";
	case -3:   return "input args is error";
	default:   return "Undefined error: $ierr";
	}
}

class COBWEB{
	var $sock;
	var $retry;
	var $svrport =array('0'=>array("0"=>16880), '1'=>array("0"=>16888));
	var $IDX;
	var $SIZE; 
	function __construct($idx=0, $retry = 3){
		$this->retry = $retry;
		$this->IDX = $idx;
		$this->SIZE = sizeof($this->svrport[$idx]);
		$this->connect();
	} 

	function connect(){
		$hostname = "10.62.162.75"; 
		if(is_resource($this->sock))
			fclose($this->sock);
		$idx = time(NULL)% $this->SIZE; 
		$this->sock = @fsockopen($hostname, $this->svrport[$this->IDX][$idx], $errno, $errstr);
		if (!$this->sock) {
			error_log("ERROR: $errno - $errstr");
			$idx = ($idx+1)% $this->SIZE;
			$this->sock = @fsockopen($hostname, $this->svrport[$this->IDX][$idx], $errno, $errstr);
			if (!$this->sock) {
				error_log("ERROR: $errno - $errstr:".$this->svrport[$this->IDX][$idx]);
				return;
			}
		}
		stream_set_timeout($this->sock, 1, 500);
	} 

	function hash($str)
	{
		$id = abs(ord($str{0}))%2 + 1;
		return $id;	
	}	


	function __destruct() { if(is_resource($this->sock))  fclose($this->sock);  }

	function graph( $user1, $user2){
		$ret = false;
		$try = 0;
		while($ret === false && $try < $this->retry)
		{
			$ret = $this->inner_graph($user1, $user2); 
			$try++;
		}
		return $ret;
	}
/*
*/
	function information( $users){
		$ret = false;
		$try = 0;
		while($ret === false && $try < $this->retry)
		{
			$ret = $this->inner_information($users); 
			$try++;
		}
		return $ret;
	}

	function friends( $user){
		$ret = false;
		$try = 0;
		while($ret === false && $try < $this->retry)
		{
			$ret = $this->inner_friends($user); 
			$try++;
		}
		return $ret;
	}
	function friends_partition( $user, $start, $num = 50){
		$ret = false;
		$try = 0;
		while($ret === false && $try < $this->retry)
		{
			$ret = $this->inner_num_friends($user, $start, $num); 
			$try++;
		}
		return $ret;
	}

	function type_friends( $user, $type, $start=0, $num=100){
		$ret = false;
		$try = 0;
		while($ret === false && $try < $this->retry)
		{
			$ret = $this->inner_type_friends($user, $type, $start, $num); 
			$try++;
		}
		return $ret;
	}

	function status(){
		$rets = array();
		for($idx = 1; $idx< 3; $idx++)
		{
			$ret = $this->inner_status($idx); 
			$try = 0;
			while(!isset($ret['1'])  && $try < $this->retry)
			{
				$ret = $this->inner_status($idx); 
				$try++;
			}
			$rets[$idx] = $ret;
		}
		return $rets;
	}

	function reset( $arr){
		if(is_array($arr) && sizeof($arr) > 0)
		{
			$isize = sizeof($arr); 
                        
			$input = "y\004".pack("I",$isize);
			foreach($arr as $k=>$v)
				$input .= pack("I",strlen($k)).$k;
			$sinput = "x".pack("I", strlen($input))."yz".$input;
			$pinput = "P".pack("I", 1 )."\002".$sinput;
                        $winput = "x".pack("I", strlen($pinput))."yz".$pinput;

			if(false == @fwrite($this->sock, $winput) )
			{
				$this->connect();
				return false;
			}

			$input2 = "P".pack("I", 2)."\002".$sinput;
                        $winput = "x".strlen($input2)."yz".$input2;
			if(false == @fwrite($this->sock, $winput))
			{
				$this->connect();
				return false;
			}

			return true;
		}
		return false;
	}

	function inner_graph( $user1, $user2){

		$hash = $this->hash($user1);
		$input = "y\003";
		$input .= pack("I",strlen($user1)).$user1;
		$input .= pack("I",strlen($user2)).$user2;
                $sinput = "x".pack("I", strlen($input))."yz".$input;
		$pinput = "P".pack("I", $hash )."\001".$sinput;
                $winput = "x".pack("I", strlen($pinput))."yz".$pinput;

		if( false == @fwrite($this->sock, $winput))
		{
			$this->connect();
			return false;
		}

		$readlen = $READ = 10240;
		$response = @fread($this->sock, $READ);
		$arr = array();
		if(isset($response{7}))
		{
			$sub = substr($response, 0, 8);
			$unpacked = unpack('I2',$sub);
			if(isset($unpacked['1']) && isset($unpacked['2']))
			{
				if($unpacked['2'] != 0)
				{
					if(strlen($response) != 8)
					{
						$this->connect();
						return false;
					}

					error_log(cobweb_getErrMsg($unpacked['2']));
					if($unpacked['2'] > 0 || $unpacked['2'] < -30 )
						return false;

					return $unpacked['2']; // return errorcode;
				}

				$left = $unpacked['1'] - strlen($response);
				while ( $left > 0 && !feof($this->sock) )
				{
					$contstr = fread ( $this->sock,  $left );
					if ($contstr )
					{
						$response .= $contstr;
						$left -= strlen($contstr);
					}
				}

				//return errorcode;
				//echo "read = "; var_dump($response);
				$idx = 8;
				$sub = substr($response, $idx , 4);
				if(!isset($sub{3}))
				{
					error_log("get depth data error!");
					return false;
				}

				$unpacked = unpack('I',$sub);
				//echo "depth = "; var_dump($unpacked);
				$idx += 4;
				$depth = $unpacked['1'];
				if($depth == 0)
					return true;
				$sub = substr($response, $idx , 4);
				if(!isset($sub{3})){
					error_log("get items counts data error");
					return false;
				}

				$unpacked = unpack('I',$sub);
				$items = $unpacked['1'];
				//echo "items = "; var_dump($unpacked);
				$idx += 4;
				// $direct = $response[$idx];
				// $idx += 1;
				for($j = 0; $j <$items ; $j++)
				{
					$subarr = array();
					for($i=0; $i<$depth; $i++)
					{
						$str = substr($response, $idx, 4);
						if(!isset($str{3})){
							error_log("get one line friends data error");
							return false;
						}

						$unpacked = unpack('Iint', $str);
						$idx += 4;
						$subarr[$i] = substr($response, $idx, $unpacked['int']);
						$idx += $unpacked['int'];
					}
					if(sizeof($subarr) > 0)
						$arr[$j] = $subarr;
				}
				if(sizeof($arr) < $items)
					return false;
			}
		}
		else
                {
                        $this->connect();
			return false; //echo "return error!";
                }
		return $arr;
	}

	function inner_information($users){
		if(!is_array($users))
		{
			error_log("input is not array!");  
			return false;
		} 

		$hash = 1;
		foreach($users as $k=>$v)
		{
			$hash = $this->hash($k);
			break;
		}

		$input = "y\006";
		$input .= pack("I",sizeof($users));
		foreach($users as $k=>$v)
			$input .= pack("I", strlen($k)).$k;

                $sinput = "x".pack("I", strlen($input))."yz".$input;
		$pinput = "P".pack("I", $hash )."\001".$sinput;
                $winput = "x".pack("I", strlen($pinput))."yz".$pinput;

		if(false == @fwrite($this->sock, $winput))
		{
			$this->connect();
			return false;
		}

		$readlen = $READ = 10240;
		$response = @fread($this->sock, $READ);
		$arr = array();
		if(isset($response{7}))
		{
			$sub = substr($response, 0, 8);
			$unpacked = unpack('I2',$sub);
			if(isset($unpacked['1']) && isset($unpacked['2']))
			{
				if($unpacked['2'] != 0)
				{
					if(strlen($response) != 8)
					{
						$this->connect();
						return false;
					}

					error_log(cobweb_getErrMsg($unpacked['2']));
					if($unpacked['2'] > 0 || $unpacked['2'] < -30 )
						return false;

					return $unpacked['2']; // return errorcode;
				}

				$left = $unpacked['1'] - strlen($response);
				while ( $left > 0 && !feof($this->sock) )
				{
					$contstr = fread ( $this->sock,  $left );
					if ( $contstr )
					{
						$response .= $contstr;
						$left -= strlen($contstr);
					}
				}

				//echo "read = "; var_dump($response);
				$idx = 8;
				$sub = substr($response, $idx , 4);
				if(!isset($sub{3})){
					error_log("get friends counts data error!");
					return false;
				}

				$unpacked = unpack('I',$sub);
				//echo "counts = "; var_dump($unpacked);
				$idx += 4;
				$counts = $unpacked['1'];
				if($counts == 0)
					return $arr;
				for($j = 0; $j<$counts ; $j++)
				{ 
					/* ulen + uname  */
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get name length data error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					$uname = substr($response, $idx, $unpacked['int']);
					$idx += $unpacked['int'];
					/* vlen + value */
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get value length data error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					if($unpacked['int'] > 0)
						$value = substr($response, $idx, $unpacked['int']);
					else
						$value = "";
					$idx += $unpacked['int'];
					/* type  */ 
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get user type error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					$arr[$uname] = array('_value'=>$value, '_num'=>$unpacked['int']);
				}
				if(sizeof($arr) < $counts)
					return false;
			}
		}
		else
		{
			$this->connect();  
			return false; //echo "return error!";
		}
		return $arr;
	}

	function inner_friends( $user){
		$hash = $this->hash($user);
		$input = "y\001";
		$input .= pack("I",strlen($user)).$user;
                
                $sinput = "x".pack("I", strlen($input))."yz".$input;
            	$pinput = "P".pack("I", $hash )."\001".$sinput;
                $winput = "x".pack("I", strlen($pinput))."yz".$pinput;

		if(false == @fwrite($this->sock, $winput))
		{
			$this->connect();
			return false;
		}
		$readlen = $READ = 10240;
		$response = @fread($this->sock, $READ);
		$arr = array();
		if(isset($response{7}))
		{
			$sub = substr($response, 0, 8);
			$unpacked = unpack('I2',$sub);
			if(isset($unpacked['1']) && isset($unpacked['2']))
			{
				if($unpacked['2'] != 0)
				{
					if(strlen($response) != 8)
					{
						$this->connect();	
						return false;
					}
					error_log(cobweb_getErrMsg($unpacked['2']));
					if($unpacked['2'] > 0 || $unpacked['2'] < -30 )
						return false;

					return $unpacked['2']; // return errorcode;
				}

				$left = $unpacked['1'] - strlen($response);
				while ( $left > 0 && !feof($this->sock) )
				{
					$contstr = fread ( $this->sock,  $left );
					if ( $contstr )
					{
						$response .= $contstr;
						$left -= strlen($contstr);
					}
				}
				//echo "read = "; var_dump($response);
				$idx = 8;
				$sub = substr($response, $idx , 4);
				if(!isset($sub{3})){
					error_log("get friends counts data error!");
					return false;
				}

				$unpacked = unpack('I',$sub);
				//echo "counts = "; var_dump($unpacked);
				$idx += 4;
				$counts = $unpacked['1'];
				if($counts == 0)
					return $arr;
				for($j = 0; $j<$counts ; $j++)
				{ 
					/* ulen + uname  */
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get firends name length data error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					$uname = substr($response, $idx, $unpacked['int']);
					$idx += $unpacked['int'];
					/* vlen + value */
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get firends value length data error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					if($unpacked['int'] > 0)
						$value = substr($response, $idx, $unpacked['int']);
					else
						$value = "";
					$idx += $unpacked['int'];
					/* type  */ 
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get user type error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					$arr[$uname] = array('_value'=>$value, '_num'=>$unpacked['int']);
				}
				if(sizeof($arr) < $counts)
					return false;
			}
		}
		else
		{
			$this->connect();
			return false; //echo "return error!";
		}
		return $arr;
	}

	function inner_type_friends( $user, $type, $start, $num){
		$hash = $this->hash($user);
		$input = "y\010";   // 8
		$input .= pack("I",strlen($user)).$user.pack("I", intval($type)).pack("I", intval($start)).pack("I", intval($num));

		$sinput = "x".pack("I", strlen($input))."yz".$input;
		$pinput = "P".pack("I", $hash )."\001".$sinput;
                $winput = "x".pack("I", strlen($pinput))."yz".$pinput;

		if(false == @fwrite($this->sock, $winput))
		{
			$this->connect();
			return false;
		}
		$readlen = $READ = 10240;
		$response = @fread($this->sock, $READ);
		$arr = array();
		if(isset($response{7}))
		{
			$sub = substr($response, 0, 8);
			$unpacked = unpack('I2',$sub);
			if(isset($unpacked['1']) && isset($unpacked['2']))
			{
				if($unpacked['2'] != 0)
				{
					if(strlen($response) != 8)
					{
						$this->connect();	
						return false;
					}
					error_log(cobweb_getErrMsg($unpacked['2']));
					if($unpacked['2'] > 0 || $unpacked['2'] < -30 )
						return false;

					return $unpacked['2']; // return errorcode;
				}

				$left = $unpacked['1'] - strlen($response);
				while ( $left > 0 && !feof($this->sock) )
				{
					$contstr = fread ( $this->sock,  $left );
					if ( $contstr )
					{
						$response .= $contstr;
						$left -= strlen($contstr);
					}
				}

				//echo "read = "; var_dump($response);
				$idx = 8;
				$sub = substr($response, $idx , 8);
				if(!isset($sub{7})){
					error_log("get friends counts data error!");
					return false;
				}

				$unpacked = unpack('I2',$sub);
				//echo "counts = "; var_dump($unpacked);
				$idx += 8;
				$arr['__counts__'] = $unpacked['1']; 
				$counts = $unpacked['2'];
				if($counts == 0)
					return $arr;
				for($j = 0; $j<$counts ; $j++)
				{ 
					/* ulen + uname  */
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get firends name length data error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					$uname = substr($response, $idx, $unpacked['int']);
					$idx += $unpacked['int'];
					/* vlen + value */
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get firends value length data error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					if($unpacked['int'] > 0)
						$value = substr($response, $idx, $unpacked['int']);
					else
						$value = "";
					$idx += $unpacked['int'];
					/* type  */ 
					$arr[$uname] = array('_value'=>$value, '_num'=>$type);
				}
				if(sizeof($arr) < $counts)
					return false;
			}
		}
		else
		{
			$this->connect();
			return false; //echo "return error!";
		}
		return $arr;
	}



	function inner_num_friends( $user, $start, $num){
		$hash = $this->hash($user);

		$input = "y\007";
		$input .= pack("I",strlen($user)).$user.pack("I",$start).pack("I", $num);
		$sinput = "x".pack("I", strlen($input))."yz".$input;
		$pinput = "P".pack("I", $hash )."\001".$sinput;
		$winput = "x".pack("I", strlen($pinput))."yz".$pinput;

		if(false == @fwrite($this->sock, $winput))
		{
			$this->connect();
			return false;
		}
		$readlen = $READ = 10240;
		$response = @fread($this->sock, $READ);
		$arr = array();
		if(isset($response{7}))
		{
			$sub = substr($response, 0, 8);
			$unpacked = unpack('I2',$sub);
			if(isset($unpacked['1']) && isset($unpacked['2']))
			{
				if($unpacked['2'] != 0)
				{
					if(strlen($response) != 8)
					{
						$this->connect();	
						return false;
					}
					error_log(cobweb_getErrMsg($unpacked['2']));
					if($unpacked['2'] > 0 || $unpacked['2'] < -30 )
						return false;

					return $unpacked['2']; // return errorcode;
				}

				$left = $unpacked['1'] - strlen($response);
				while ( $left > 0 && !feof($this->sock) )
				{
					$contstr = fread ( $this->sock,  $left );
					if ( $contstr )
					{
						$response .= $contstr;
						$left -= strlen($contstr);
					}
				}

				//echo "read = "; var_dump($response);
				$idx = 8;
				$sub = substr($response, $idx , 8);
				if(!isset($sub{7})){
					error_log("get friends counts data error!");
					return false;
				}

				$unpacked = unpack('I2',$sub);
				//echo "counts = "; var_dump($unpacked);
				$idx += 8;

				$allcounts = $unpacked['1'];
				if($allcounts == 0)
					return $arr;
                                $arr['__counts__'] = $allcounts;
                               	$counts = $unpacked['2'];
				if($counts == 0)
					return $arr;
				for($j = 0; $j<$counts ; $j++)
				{ 
					/* ulen + uname  */
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get firends name length data error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					$uname = substr($response, $idx, $unpacked['int']);
					$idx += $unpacked['int'];
					/* vlen + value */
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get firends value length data error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					if($unpacked['int'] > 0)
						$value = substr($response, $idx, $unpacked['int']);
					else
						$value = "";
					$idx += $unpacked['int'];
					/* type  */ 
					$str = substr($response, $idx, 4);
					if(!isset($str{3})){
						error_log("get user type error");
						return false;
					}
					$unpacked = unpack('Iint', $str);
					$idx += 4;
					$arr[$uname] = array('_value'=>$value, '_num'=>$unpacked['int']);
				}
				if(sizeof($arr) < $counts)
					return false;
			}
		}
		else
		{
			$this->connect();
			return false; //echo "return error!";
		}
		return $arr;
	}



	function inner_status( $idx ){
		$input = "y\005status";
		$sinput = "x".pack("I", strlen($input))."yz".$input;
		$pinput = "P".pack("I", $idx )."\001".$sinput;
		$winput = "x".pack("I", strlen($pinput))."yz".$pinput;

		if(false == @fwrite($this->sock, $winput))
		{
			$this->connect();
			return false;
		}

		$readlen = $READ = 10240;
		$response = @fread($this->sock, $READ);
		$arr = array();
		if(isset($response{7}))  // length + code + ucounts + kicktime + limit + reqcount + starttime
		{
			$sub = substr($response, 0, 8);
			$unpacked = unpack('I2',$sub);
			if(isset($unpacked['1']) && isset($unpacked['2']))
			{
				if($unpacked['2'] != 0)
					return false; // return errorcode;
				//	echo "read = "; var_dump($response);
				$idx = 8;
                                
				$sub = substr($response, $idx , 20);
				if(!isset($sub{19}))
					return false;
				$arr = unpack('I5',$sub);
				$idx += 20;
				$sub = substr($response, $idx, 4);
				$idx += 4;
				if(isset($sub{3}))
				{
					$nlen = unpack('I',$sub);
					$arr['server'] =  substr($response, $idx, $nlen['1']);
					$idx += $nlen['1'];
				}
				else
					$arr['server'] = 'unkown';

				$sub = substr($response, $idx, 4);
				$idx += 4;
				if(isset($sub{3}))
				{
					$nlen = unpack('I',$sub);
					$arr['version'] =  substr($response, $idx, $nlen['1']);
				}
				else
					$arr['version'] = 'unkown';
			}
			if(sizeof($arr)< 1)
				return false;
		}
		else
		{
			$this->connect();
			return false; //echo "return error!";
		}
		return $arr;
	}
}

/*

$obj = new COBWEB(16880);

$ret = $obj->friends("1973");
var_dump($ret);

$ret = $obj->friends_partition ("1973", 0, 10);
var_dump($ret);

$ret = $obj->information(array('1973'=>'', '2'=>''));
var_dump($ret);

$ret = $obj->graph('1973','12');
var_dump($ret);
*/

?>
